package com.lilidp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lilidp.dto.Result;
import com.lilidp.entity.Shop;
import com.lilidp.mapper.ShopMapper;
import com.lilidp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lilidp.utils.RedisConstants;
import com.lilidp.utils.RedisData;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 栗顺
 
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.MINUTES);
        return flag;
    }

    private void unlock(String key){
        stringRedisTemplate.delete(key);
    }
    @Override
    public Result queryById(Long id) {
        Shop shop = queryWithMutex(id);
        if(shop == null){
            return Result.fail("获取店铺失败");
        }
        //返回结果
        return Result.ok(shop);
    }

    public Shop queryWithMutex(Long id) {
        //先查redis
        String key= RedisConstants.CACHE_SHOP_KEY+id;
        String string = stringRedisTemplate.opsForValue().get(key);
        //查询到了，直接返回结果
        if(StrUtil.isNotBlank(string)){
            Shop bean = JSONUtil.toBean(string, Shop.class);
            return bean;
        }
        if(string!=null){
            return null;
        }
        String lockkey=RedisConstants.LOCK_SHOP_KEY+id;
        Shop shop=null;
        try{
            boolean flag = tryLock(lockkey);
            if(!flag){
                Thread.sleep(50);
                return queryWithMutex(id);
            }
            String string1 = stringRedisTemplate.opsForValue().get(key);
            //查询到了，直接返回结果
            if(StrUtil.isNotBlank(string1)){
                Shop bean = JSONUtil.toBean(string1, Shop.class);
                return bean;
            }

            //查询不到，查询数据库
            shop = getById(id);
            if(shop==null){
                stringRedisTemplate.opsForValue().set(key,"", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
                return null;
            }
            //回设缓存
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            unlock(lockkey);
        }

        //返回结果
        return shop;
    }

    private ExecutorService threadPool= Executors.newFixedThreadPool(10);
    public Shop queryWithLogicalExpire(Long id) {
        //先查redis
        String key= RedisConstants.CACHE_SHOP_KEY+id;
        String string = stringRedisTemplate.opsForValue().get(key);
        //查询到了，直接返回结果
        if(StrUtil.isBlank(string)){
            return null;
        }
        //命中

        String str=stringRedisTemplate.opsForValue().get(key);
        RedisData redisData = JSONUtil.toBean(str, RedisData.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
        if(expireTime.isAfter(LocalDateTime.now())){
            return shop;
        }
        //缓存已过期，开启另外一个线程，同步数据
        String lockkey=RedisConstants.LOCK_SHOP_KEY+id;
        boolean flag = tryLock(lockkey);
        if(flag){
            threadPool.submit(()->{
                try {
                    this.saveShop2Redis(id,20L);
                }finally {
                    unlock(lockkey);
                }
            });
        }
        //返回过期结果
        return shop;
    }



    public void saveShop2Redis(Long id,Long expireSeconds){
        //首先查询数据库
        Shop shop = getById(id);
        //封装逻辑过期时间
        RedisData redisData=new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
        //写入redis
        stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY+id, JSONUtil.toJsonStr(redisData));
    }

    public Shop PassThrough(Long id) {
        //先查redis
        String key= RedisConstants.CACHE_SHOP_KEY+id;
        String string = stringRedisTemplate.opsForValue().get(key);
        //查询到了，直接返回结果
        if(StrUtil.isNotBlank(string)){
            Shop bean = JSONUtil.toBean(string, Shop.class);
            return bean;
        }
        if(string!=null){
            return null;

        }
        //查询不到，查询数据库
        Shop shop = getById(id);
        if(shop==null){
            stringRedisTemplate.opsForValue().set(key,"", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }
        //回设缓存
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //返回结果
        return shop;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result updateByIdAndupdateCache(Shop shop) {
        Long shopId=shop.getId();
        if(shopId==null){
            Result.fail("商铺不存在");
        }
        String key= RedisConstants.CACHE_SHOP_KEY+shopId;
        // 写入数据库
        updateById(shop);
        //删除缓存
        stringRedisTemplate.delete(key);
        return Result.ok();
    }


}
